當我在嘗試用類陣列使用 push()
的時候,發現居然可以欸?!
好,我明白可能是因爲類陣列偽裝得太像甚至騙過了陣列方法,所以才可以用,所以我去查詢ECMA對陣列方法的規範,
於是我看到這段話:
The push function is intentionally generic; it does not require that its this value be an Array object. Therefore it can be transferred to other kinds of objects for use as a method.
我還以為陣列方法的標準之嚴格只能被陣列使用的,但陣列方法居然挑明地說就是故意讓大家都可以使用拉~~
WHAT?! OK, let's go into it ~~
不過先來介紹一下Array.prototype
的用法
不知道你有沒有想過,為什麼在使用陣列的時候可以這樣直接在array.push()
直接這樣做:
a.push(4)
console.log(a)
// [1,2,3,4]
可是查詢MDN的時候,全名應該是:Array.prototype.push()
,跟我原本用的方式很不一樣耶,這樣要怎麼用?
其實是可以用 Array.prototype.push()
這樣的方式去寫!
只是用這個方式的話,我要把要執行的陣列放在哪裡呢?
const a = [1,2,3]
Array.prototype.push.call(a, 4)
console.log(a)
// [1,2,3,4]
call()
來綁定this
值,就是要用call()
來綁定現在要執行的陣列
call()
來綁定現在的this
值,而原本要push的值再放到第二個參數,這種寫法也可以有一樣的效果。原本在學
call()
的時候都是用在function身上,原來他也可以用在陣列。
MDN - Thecall()
allows for a function/method belonging to one object to be assigned and called for a different object.
好,這種pop()
, push()
, shift()
, unshift()
這種比較容易,只是傳一個值或者不傳,來試試看參數要帶入有callback function的方法。
const ac = Array(1, 2, 3);
const bc = Array.prototype.map.call(ac, (item) => item + 1);
console.log(bc);
// [ 2, 3, 4 ]
其實跟正常使用map的方式一樣,只是把 function放在this
的後面而已,call()
就會來用array.prototype.map()
的方式去執行了。
那...object也可以嗎?
終於要進入正題了
前言有提到,查詢ECMA的時候幾乎都有一個說法:
The push function is intentionally generic; it does not require that its this value be an Array object. Therefore it can be transferred to other kinds of objects for use as a method.
自己翻譯:
push函式是有意通用的,他並不會要求一定要是一個陣列。所以他可以被轉變成其他的物件來當作方法使用。
這是在鼓勵我去把method用在不是array的物件上嗎?
var arrayLike = {
0: 2,
1: 4,
2: 6,
};
Array.prototype.push.call(arrayLike, 8);
console.log(arrayLike);
// {
// '0': 8,
// '1': 4,
// '2': 6,
// length: 1
// }
可以是可以啊,可是變得超怪的啊!
居然原本的 key:0
的 value 被替換成新 push 進去的 8 ?
然後自動產生length: 1,可是明明就有3個property!
看起來是他只認後來使用方法進去的值。
push進去的值不斷地取代一開始我自訂的值,終於取代完之後終於開始從後面push值進去,而length在全部都取代完之後也正常了..
var arrayLike = {
0: 2,
1: 4,
2: 6,
length: 3,
};
Array.prototype.push.call(arrayLike, 8);
console.log(arrayLike);
// { '0': 2, '1': 4, '2': 6, '3': 8, length: 4 }
一切都正常了,namaste
來試試看很困難需要遍歷的map()
, reduce()
吧
const newArrayLike = Array.prototype.map.call(arrayLike, (item) => item + 1);
console.log(newArrayLike);
// [ 3, 5, 7, 9 ]
他回來了!可是自動變成陣列了
const newArrayLike = Array.prototype.reduce.call(
arrayLike,
function (current, previous) {
current += previous;
return current;
},
0
);
console.log(newArrayLike);
// 12
居然...也是可以?!
可是在迭代那篇文章中,才說到由於陣列跟物件儲存的方式不一樣,陣列是可以被遍歷的,但物件是沒辦法的,所以他沒有iterable是很正常的,這邊我都能夠理解。
可是現在這個array-like object可以被map()
、reduce()
遍歷耶!!
我想應該是因為有 length 的關係,只要有length JS就可以盡力把它做到陣列可以做到的功能。畢竟JS就是個很沒節操的語言啊~
好了,大家腦袋是不是更混亂了呢?
為什麼我一直在打臉自己的文章啊!!
但是反正,就是不要把object做array做的事!
不然至少把他用Array.from()
把他變成一個陣列再做吧!
(但是我一直做,瘋掉)
明天見拉!
參考資料:What does Array.prototype.join.call do in the background for a string?
Array.prototype.concat is not generic
MDN
ECMA